package com.zwy.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zwy.constants.*;
import com.zwy.domain.dto.UserRegisterDTO;
import com.zwy.domain.entity.*;
import com.zwy.domain.response.ResponseResult;
import com.zwy.domain.vo.UserAccountVO;
import com.zwy.enums.*;
import com.zwy.mapper.*;
import com.zwy.service.IpService;
import com.zwy.service.UserService;
import com.zwy.utils.HttpUtils;
import com.zwy.utils.IpUtils;
import com.zwy.utils.RedisCache;
import com.zwy.utils.SecurityUtils;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Headers;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 *  *(User)表服务实现类
 * @author zwy
 * @version 1.0
 * @description: UserServiceImpl
 * @date 2025/9/18 16:04
 */
@Slf4j
@Service("userService")
public class UserServiceImpl  extends ServiceImpl<UserMapper , User> implements UserService {
    @Resource
    private UserMapper userMapper;

    @Resource
    private RoleMapper roleMapper;

    @Resource
    private RolePermissionMapper rolePermissionMapper;

    @Resource
    private PermissionMapper permissionMapper;


    @Resource
    private PasswordEncoder passwordEncoder;


    @Resource
    private UserRoleMapper userRoleMapper;
    @Resource
    private IpService ipService;
    @Resource
    private RedisCache redisCache;



    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        HttpServletRequest request = SecurityUtils.getCurrentHttpRequest();
        String equipmentHeader = null;
        String typeHeader = null;
        String accessToken = null;
        if (request != null) {
            equipmentHeader = request.getHeader(Const.TYPE_HEADER);
            typeHeader = request.getHeader(Const.FRONTEND_LOGIN_TYPE);
            accessToken = request.getHeader(Const.FRONTEND_THIRD_LOGIN_TOKEN);
        }
        User user = null;
        // 判断是否第三方登录
        if (typeHeader != null) {
            // getee
            if (typeHeader.equals(RegisterOrLoginTypeEnum.GITEE.getStrategy())) {
                String result = HttpUtils.sendGet(UrlEnum.GITEE_USER_INFO.getUrl(), "access_token=" + accessToken);
                JSONObject jsonObject = JSON.parseObject(result);
                Integer uuid = (Integer) jsonObject.get(SQLConst.ID);
                user = userMapper.selectById(uuid);
            }
            // github
            if (typeHeader.equals(RegisterOrLoginTypeEnum.GITHUB.getStrategy())) {
                OkHttpClient client = new OkHttpClient();
                Headers headers = new Headers.Builder()
                        .add(RequestHeaderEnum.GITHUB_USER_INFO.getHeader(), RequestHeaderEnum.GITHUB_USER_INFO.getContent())
                        .add(RespConst.TOKEN_HEADER, RespConst.TOKEN_PREFIX + accessToken)
                        .build();
                Request getRequest = new Request.Builder()
                        .url(UrlEnum.GITHUB_USER_INFO.getUrl())
                        .method(UrlEnum.GITHUB_USER_INFO.getMethod(), null)
                        .headers(headers)
                        .build();
                try (Response response = client.newCall(getRequest).execute()) {
                    JSONObject jsonObject;
                    if (response.body() != null) {
                        jsonObject = JSON.parseObject(response.body().string());
                        Integer uuid = (Integer) jsonObject.get(SQLConst.ID);
                        user = userMapper.selectById(uuid);
                    }
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }

            }
        } else {
            user = findAccountByNameOrEmail(username);
        }
        // 2. 判断用户是否存在
        if (ObjectUtils.isEmpty(user)) {
            // 不存在，抛出异常
            throw new UsernameNotFoundException(RespConst.USERNAME_OR_PASSWORD_ERROR_MSG);
        }
        return handlerLogin(user, equipmentHeader);
    }

    /**
     * 根据用户名或邮箱查询用户
     * @param text 用户名或邮箱
     * @return 用户实体
     */
    @Override
    public User findAccountByNameOrEmail(String text) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getUsername, text).or().eq(User::getEmail, text).eq(User::getRegisterType, RegisterOrLoginTypeEnum.EMAIL.getRegisterType());
        return userMapper.selectOne(wrapper);
    }

    /**
     * 处理用户登录逻辑，生成包含权限信息的LoginUser对象
     * @param user 用户实体
     * @param equipmentHeader 请求头中的设备类型标识（来自loadUserByUsername）
     * @return 包含用户信息和权限的LoginUser对象
     * @throws BadCredentialsException 如果用户被禁用/测试账号非法访问/无权限时抛出
     */
    public LoginUser handlerLogin(User user, String equipmentHeader) {
        // 获取当前请求并提取请求头
        HttpServletRequest request = SecurityUtils.getCurrentHttpRequest();


        // 查询用户角色列表（过滤掉状态无效的角色）
        List<UserRole> userRoles = userRoleMapper.selectList(new LambdaQueryWrapper<UserRole>().eq(UserRole::getUserId, user.getId()));
        List<Role> roles = userRoles.stream()
                .map(role -> roleMapper.selectById(role.getRoleId()))
                .filter(role -> Objects.equals(role.getStatus(), RoleEnum.Role_STATUS_ARTICLE.getStatus()))
                .toList();

        // 1. 检查用户是否被禁用
        if (user.getIsDisable() == 1) {
            throw new BadCredentialsException(RespConst.ACCOUNT_DISABLED_MSG);
        }

        // 2. 测试账号校验：测试账号只能通过后端请求访问
        if (equipmentHeader == null || (roles.stream().anyMatch(role -> role.getRoleKey().equals(SecurityConst.ROLE_TESTER))
                && !equipmentHeader.equals(Const.BACKEND_REQUEST))) {
            throw new BadCredentialsException(RespConst.TEST_ACCOUNT_MSG);
        }

        // 3. 权限校验：后端请求必须要有角色权限
        if ((equipmentHeader != null && equipmentHeader.equals(Const.BACKEND_REQUEST)
                && ObjectUtils.isEmpty(roles))) {
            throw new BadCredentialsException(RespConst.NO_PERMISSION_MSG);
        }

        // 组合权限数据
        if (!roles.isEmpty()) {
            // 查询角色对应的权限关系
            List<RolePermission> rolePermissions = rolePermissionMapper.selectBatchIds(
                    roles.stream().map(Role::getId).toList());

            // 提取权限ID并查询具体权限
            List<Long> pIds = rolePermissions.stream()
                    .map(RolePermission::getPermissionId)
                    .toList();
            List<Permission> permissions = permissionMapper.selectBatchIds(pIds);

            // 组合权限标识（格式：权限key + ROLE_角色key）
            List<String> list = permissions.stream()
                    .map(Permission::getPermissionKey)
                    .collect(Collectors.toList());
            roles.forEach(role -> list.add(SecurityConst.ROLE_PREFIX + role.getRoleKey()));

            return new LoginUser(user, list); // 返回带权限的用户对象
        }

        return new LoginUser(user, List.of()); // 返回无额外权限的用户对象
    }

    // 修改用户登录或注册状态
    @Override
    public void userLoginStatus(Long id, Integer type) {
        // ip地址
        String ipAddr = IpUtils.getIpAddr(SecurityUtils.getCurrentHttpRequest());
        if (IpUtils.isUnknown(ipAddr)) {
            ipAddr = IpUtils.getHostIp();
        }
        User user = User.builder()
                .id(id)
                .loginTime(new Date())
                .loginType(type)
                .loginIp(ipAddr)
                .build();
        if (updateById(user)) {
            ipService.refreshIpDetailAsyncByUidAndLogin(user.getId());
        }

    }
    @Override
    public UserAccountVO findAccountById(Long id) {
        log.info("findAccountById: {}", id);
        User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getId, id));
        log.info("user: {}", user);
        List<String> userRoles = SecurityUtils.getUserRoles();
        // 角色
        List<String> roles = new ArrayList<>();
        // 权限
        List<String> permissions = new ArrayList<>();
        userRoles.forEach(role -> {
            if (role.startsWith(SecurityConst.ROLE_PREFIX)) {
                // 去掉前缀，添加
                roles.add(role.substring(SecurityConst.ROLE_PREFIX.length()));
            } else {
                permissions.add(role);
            }
        });

        return user.asViewObject(UserAccountVO.class, role -> {
            role.setRoles(roles);
            role.setPermissions(permissions);
        });
    }

    /**
     * 用户注册
     *
     * @param userRegisterDTO 参数
     * @return 是否成功
     */
    @Override
    public ResponseResult<Void> userRegister(UserRegisterDTO userRegisterDTO) {
        // 1.判断验证码
        ResponseResult<Void> verifyCode = verifyCode(userRegisterDTO.getEmail(), userRegisterDTO.getCode(), RedisConst.REGISTER);
        if (verifyCode != null) return verifyCode;
        // 2.判断用户名或邮箱是否已存在
        if (userIsExist(userRegisterDTO.getUsername(), userRegisterDTO.getEmail())) {
            return ResponseResult.failure(RespEnum.USERNAME_OR_EMAIL_EXIST.getCode(), RespEnum.USERNAME_OR_EMAIL_EXIST.getMsg());
        }
        // 3.密码加密
            String enPassword = passwordEncoder.encode(userRegisterDTO.getPassword());
            Date date = new Date();


            // 获取注册ip地址
            String ipAddr = IpUtils.getIpAddr(SecurityUtils.getCurrentHttpRequest());
            if (IpUtils.isUnknown(ipAddr)) {
                ipAddr = IpUtils.getHostIp();
            }
            // 4.保存用户信息
            User user = User.builder()
                    .id(null)
                    .nickname(userRegisterDTO.getUsername())
                    .username(userRegisterDTO.getUsername())
                    .password(enPassword)
                    .registerType(RegisterOrLoginTypeEnum.EMAIL.getRegisterType())
                    .registerIp(ipAddr)
                    .gender(UserConst.DEFAULT_GENDER)
                    .avatar(UserConst.DEFAULT_AVATAR)
                    .intro(UserConst.DEFAULT_INTRODUCTION)
                    .registerType(RegisterOrLoginTypeEnum.EMAIL.getRegisterType())
                    .isDeleted(UserConst.DEFAULT_STATUS)
                    .email(userRegisterDTO.getEmail())
                    .loginTime(date).build();
            if (this.save(user)) {
                // 异步刷新ip详情获取
                ipService.refreshIpDetailAsyncByUidAndRegister(user.getId());
                // 删除验证码
                redisCache.deleteObject(RedisConst.VERIFY_CODE + RedisConst.REGISTER + RedisConst.SEPARATOR + userRegisterDTO.getEmail());
                return ResponseResult.success();
            } else {
                return ResponseResult.failure();
            }


    }
/**
 * 判断验证码是否正确
 */
private ResponseResult<Void> verifyCode(String email,String code, String type){

    String redisCode = redisCache.getCacheObject(RedisConst.VERIFY_CODE + type + RedisConst.SEPARATOR + email);
    if (redisCode == null)
        return ResponseResult.failure(RespEnum.VERIFY_CODE_ERROR.getCode(), RespConst.VERIFY_CODE_NULL_MSG);
    if (!redisCode.equals(code))
        return ResponseResult.failure(RespEnum.VERIFY_CODE_ERROR.getCode(), RespEnum.VERIFY_CODE_ERROR.getMsg());
    return null;
}

    /**
     * 判断用户名或邮箱是否已存在
     *
     * @param username 用户名
     * @param email    邮箱
     * @return boolean
     */
    private boolean userIsExist(String username, String email) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getUsername, username).or().eq(User::getEmail, email);
        return this.userMapper.selectOne(wrapper) != null;
    }
}
